package com.vois.jack.btmgr.devices.WLBtSBCDevice;

import android.os.Bundle;
import android.os.Message;
import android.util.Log;

import com.vois.jack.btmgr.classicbase.BtDevCommonMsg;
import com.vois.jack.btmgr.classicbase.BtDevConstant;
import com.vois.jack.btmgr.classicbase.BtRecorderInterface;
import com.vois.jack.btmgr.classicbase.BtSBCCodecRecorder;
import com.vois.jack.btmgr.classicbase.BtScoRecorder;
import com.vois.jack.btmgr.common.DeviceRecorderControlInterface;
import com.vois.jack.btmgr.devices.WLBtBtnDevice.WLBtBtnDevice;
import com.vois.jack.btmgr.util.ActionProcessUtil;
import com.vois.jack.btmgr.util.Logger;
import com.voistech.sbccoder.SBCCodec;

public class WLBtSBCDevice extends WLBtBtnDevice implements DeviceRecorderControlInterface {
    private Logger logger = Logger.getLogger(WLBtSBCDevice.class);
    private BtSBCCodecRecorder sbcCodecRecorder;
    private BtScoRecorder btScoRecorder;
    private byte[] mediaParam;
    private ActionProcessUtil actionProcessUtil;
    private ResultCallback startRecorderCb;
    private ResultCallback stopRecorderCb;
    private SBCCoderParameter sbcCoderParameter;
    private boolean usingSBCCoder;

    class SBCCoderParameter {
        int sampleRate;
        int blocks;
        int channelMode;
        int am;
        int subBand;
        int bitPool;

        boolean setCoderParamter(int sampleRate, int blocks, int channelMode, int am, int subband, int bitpool) {
            if (sampleRate >= SBCCodec.SBC_FREQ_16000 && sampleRate <= SBCCodec.SBC_FREQ_48000) {
                this.sampleRate = sampleRate;
            }else {
                return false;
            }

            if (blocks >= SBCCodec.SBC_BLK_4 && blocks <= SBCCodec.SBC_BLK_16) {
                this.blocks = blocks;
            }else {
                return false;
            }

            if (channelMode >= SBCCodec.SBC_MODE_MONO && channelMode <= SBCCodec.SBC_MODE_JOINT_STEREO) {
                this.channelMode = channelMode;
            }else {
                return false;
            }

            if (am >= SBCCodec.SBC_AM_LOUDNESS && am <= SBCCodec.SBC_AM_SNR) {
                this.am = am;
            }else {
                return false;
            }

            if (subband >= SBCCodec.SBC_SB_4 && subband <= SBCCodec.SBC_SB_8) {
                this.subBand = subband;
            }else {
                return false;
            }

            if (bitpool >=0 && bitpool <= 32) {
                this.bitPool = bitpool;
            }else {
                return false;
            }

            return true;
        }
    }

    @Override
    public void openDevice() {
        super.openDevice();
        actionProcessUtil = new ActionProcessUtil("BtSbc", getBtDevFsm().getHandler());
        actionProcessUtil.startActionExecution();
        usingSBCCoder = false;
        sbcCoderParameter = new SBCCoderParameter();
    }

    @Override
    public void closeDevice() {
        super.closeDevice();
        if (actionProcessUtil != null) {
            actionProcessUtil.stopActionExecution();
            actionProcessUtil = null;
        }
    }

    @Override
    public BtRecorderInterface getRecorder(int recorderType) {
        if (recorderType == SBC_RECORDER) {
            sbcCodecRecorder = new BtSBCCodecRecorder();
            sbcCodecRecorder.setSbcParameters(sbcCoderParameter.sampleRate, sbcCoderParameter.blocks,
                    sbcCoderParameter.channelMode, sbcCoderParameter.am,
                    sbcCoderParameter.subBand, sbcCoderParameter.bitPool);
            return sbcCodecRecorder;
        }else if (recorderType == SCO_RECORDER) {
            btScoRecorder = new BtScoRecorder();
            return btScoRecorder;
        }

        return null;
    }

    @Override
    public int getDefaultRecorderType() {
        if (usingSBCCoder) {
            return SBC_RECORDER;
        }

        return SCO_RECORDER;
    }

    @Override
    public boolean startRecorder(ResultCallback callback) {
        startRecorderCb = callback;
        if (usingSBCCoder) {
            sendCommand(0x34, null, 0);
        }else {
            return super.startRecorder(callback);
        }

        return true;
    }

    @Override
    public void stopRecorder(ResultCallback callback) {
        if (startRecorderCb != null) {
            startRecorderCb.onResult(false);
            startRecorderCb = null;
        }

        stopRecorderCb = callback;
        if (usingSBCCoder) {
            sendCommand(0x35, null, 0);
        }else {
            super.stopRecorder(callback);
        }
    }

    @Override
    public boolean isNeedHeartbeat() {
        return !usingSBCCoder;
    }

    //    private void debugInfo(String debugInfo) {
//        if (getListener() != null) {
//            Message message = Message.obtain();
//            message.what = BtDevCommonMsg.BT_DEV_DEBUG_INFO.getValue();
//            Bundle bundle = new Bundle();
//            bundle.putString(BtDevConstant.EXTRA_DEBUG_STR, debugInfo);
//            message.setData(bundle);
//            getListener().onMessage(this, message);
//        }
//    }

//    private String dumpArray(byte[] data, int datalen) {
//        int dataSize = datalen;
//        char[] out = new char[dataSize * 3];
//        char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
//        StringBuffer debugStr = new StringBuffer();
//        for (int i = 0, j = 0; i < dataSize; i++) {
//            out[j++] = DIGITS_UPPER[(0xF0 & data[i]) >>> 4];
//            out[j++] = DIGITS_UPPER[0x0F & data[i]];
//            out[j++] = ',';
//        }
//
//        debugStr.append("-------------[" + dataSize + "]-----------------\n");
//        debugStr.append(new String(out));
//        debugStr.append("\n------------------------------------------\n");
//
//        return debugStr.toString();
//    }

    @Override
    protected boolean processCommand() {
        boolean ret = super.processCommand();

        if (ret) {
            return true;
        }

        switch (curCommand) {
            case 0x2B: {
                ret = true;
            }
            break;


            case 0x34: {
                // start record responce
                int result = curContentData[0] & 0xFF;

                if (startRecorderCb != null) {
                    startRecorderCb.onResult(result == 1);
                    startRecorderCb = null;
                }

                ret = true;
            }
            break;

            case 0x35: {
                // stop record responce
                int result = curContentData[0] & 0xFF;
                //TODO: notify seq id written result;
                if (stopRecorderCb != null) {
                    stopRecorderCb.onResult(result == 1);
                    stopRecorderCb = null;
                }

                ret = true;
            }
            break;

            case 0xEE: {
                //audio incoming
                //logger.dumpArray(curContentData, curContentLen);
                if (sbcCodecRecorder != null && curContentData != null) {
                    sbcCodecRecorder.putEncodedRecordData(curContentData, 0, curContentLen);
                }

                ret = true;
            }
            break;

            case 0x1F: {
                mediaParam = new byte[curContentLen - 2];
                System.arraycopy(curContentData, 0, mediaParam, 0, curContentLen - 2);
                if (mediaParam[0] == 0x02) {
                    if (mediaParam.length == 7) {
                        boolean result = sbcCoderParameter.setCoderParamter(mediaParam[1], mediaParam[2],
                                mediaParam[3], mediaParam[4], mediaParam[5], mediaParam[6]);
                        if (!result) {
                            logger.e("parameters aren't valid");
                        }else {
                            usingSBCCoder = true;
                            logger.d("sbc parameter is set!");
                        }
                    }else {
                        logger.e("media params aren't valid");
                    }
                }else {
                    logger.e("it is not sbc media params");
                }

                if (actionProcessUtil != null) {
                    actionProcessUtil.reportResult(this, null);
                }

                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_GET_MEDIA_FEATURE_RET.getValue();
                    getListener().onMessage(this, message);
                }
            }
            break;
        }

        return ret;
    }

    private void querySupportMediaAbility() {
        if (actionProcessUtil != null) {
            actionProcessUtil.addActionRequest(this, null, 300, new ActionProcessUtil.ActionCallback() {
                @Override
                public boolean onProcess(Object context, Bundle parameters) {
                    sendCommand(0x1F, null, 0);
                    return true;
                }

                @Override
                public void onResult(Object context, Bundle data) {

                }

                @Override
                public void onError(Object context, int errorCode) {

                }

                @Override
                public void onTimeout(Object context) {
                    querySupportMediaAbility();
                }
            });
        }
    }

    @Override
    protected void onSppSocketConnected() {
        super.onSppSocketConnected();
        querySupportMediaAbility();
    }

    @Override
    public boolean useOwnPlayTone() {
        return usingSBCCoder;
    }
}
